package indi.zhk.nocturne.ioc.factory;

import indi.zhk.nocturne.ioc.entity.BeanDefinition;
import indi.zhk.nocturne.ioc.entity.BeanReference;
import indi.zhk.nocturne.ioc.entity.PropertyValue;

import java.lang.reflect.Field;
import java.util.Map;

/**
 * @author 赵洪苛
 * @date 2021/6/19 18:04
 **/
public class AutowiredCapableBeanFactory extends AbstractBeanFactory {

    /**
     * 创建bean实例对象
     *
     * @param beanDefinition bean定义对象
     * @return bean实例对象
     */
    @Override
    protected Object doCreateBean(BeanDefinition beanDefinition) {
        if (beanDefinition.getSingleton() && beanDefinition.getBean() != null) {
            return beanDefinition.getBean();
        }
        Object bean = null;
        try {
            bean = beanDefinition.getBeanClass().newInstance();
            // 如果该对象为单例，则将其填充至包装对象中
            if (beanDefinition.getSingleton()) {
                beanDefinition.setBean(bean);
            }
            // 给对象属性赋值
            applyPropertyValues(bean, beanDefinition);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return bean;
    }

    /**
     * 为新创建的bean注入属性
     * @param bean 待注入属性的bean
     * @param beanDefinition bean的定义
     */
    protected void applyPropertyValues(Object bean, BeanDefinition beanDefinition) throws Exception {
        for (PropertyValue propertyValue : beanDefinition.getPropertyValues().getPropertyValues()) {
            Field field = bean.getClass().getDeclaredField(propertyValue.getName());
            Object value = propertyValue.getValue();
            // 如果该属性是ioc容器中的对象
            if (value instanceof BeanFactory) {
                BeanReference beanReference = (BeanReference) value;
                BeanDefinition refDefinition = null;
                if (beanDefinitionMap.containsKey(beanReference.getName())) {
                    // 能根据实例ID找到对象
                    refDefinition = beanDefinitionMap.get(beanReference.getName());
                } else {
                    // 不能根据实例ID找到对象，则遍历容器，寻找继承该类的对象
                    for (Map.Entry<String, BeanDefinition> entry : beanDefinitionMap.entrySet()) {
                        if (beanReference.getClazz().isAssignableFrom(entry.getValue().getBeanClass())) {
                            refDefinition = entry.getValue();
                            break;
                        }
                    }
                }
                if (refDefinition != null) {
                    value = refDefinition.getBean() == null ? doCreateBean(refDefinition) : refDefinition.getBean();
                }
            }
            field.setAccessible(true);
            setField(field, bean, value);
        }
    }

    protected void setField(Field field, Object bean, Object value) throws IllegalAccessException {
        Class<?> clazz = field.getType();
        if (Integer.class.equals(clazz)) {
            field.set(bean, Integer.parseInt(value.toString()));
        } else if (Long.class.equals(clazz)) {
            field.set(bean, Long.parseLong(value.toString()));
        } else if (Float.class.equals(clazz)) {
            field.set(bean, Float.parseFloat(value.toString()));
        } else if (Double.class.equals(clazz)) {
            field.set(bean, Double.parseDouble(value.toString()));
        } else if (Short.class.equals(clazz)) {
            field.set(bean, Short.parseShort(value.toString()));
        } else if (Boolean.class.equals(clazz)) {
            field.set(bean, Boolean.parseBoolean(value.toString()));
        } else if (Byte.class.equals(clazz)) {
            field.set(bean, Byte.parseByte(value.toString()));
        } else {
            field.set(bean, value);
        }
    }
}
